<?php

/*
 * 数据表 - 数据迁移
 * @author birdy <freebirdy@foxmail.com>
 * @link http://www.aiyuangong.com
 */

namespace app\admin\command\transfer;

use app\admin\command\Base;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\Db;
use think\Cache;

class Table extends Base
{
    private $images = [];
    private $dbOld = null;
    private $mulInsert = 500; //一次插入多少数据
    private $options = [];
    
    protected $imagesFile = RUNTIME_PATH . '/transfer_images.php';

    /**
     * 处理任务计划中的待执行任务
     * 
     * @return void
     */
    public function execute(Input $input, Output $output)
    {
        config('log.type', 'test'); //临时关闭日志
        
        $config = require __DIR__.'/config.php';
        
        $this->dbOld = Db::connect('old_db');
        $this->_showLog("data transfer start", 'step');
        
        $this->options = $input->getOptions();
        $tables = $this->options['table'];
        if ($tables) {
            $tables = explode(',', $tables);
        }
        
        //数据迁移
        foreach ($config as $table => $config) {
            if ($tables && !in_array($table, $tables)) {
                continue;
            }

            if ($config === 'all') {
                $this->run_all($table, $table);
            } else if (is_array($config)) {
                $rule = $config['rule'];
                $fields = isset($config['fields']) ? $config['fields'] : [];
                $oldtable = isset($config['table']) ? $config['table'] : $table;

                if ($rule === 'all') {
                    $this->run_all($table, $oldtable);
                } else if ($rule === 'common') {
                    $this->run_common($table, $oldtable, $fields);
                } else if (method_exists($this, 'run_' . $rule)) {
                    $method = 'run_' . $rule;
                    $this->$method($table, $oldtable, $fields);
                } else {
                    $this->_showLog('unknow rule :'.$rule, 'error');
                }
            } else if ($config === 'pass') {
                $this->_showLog($table . " do not need to transfer", 'step');
            } else if (method_exists($this, 'run_' . $config)) {
                $method = 'run_' . $config;
                $this->$method($table, $table);
            } else {
                $this->_showLog('unknow rule :'.$config, 'error');
            }
        }
        
        //生成图片处理缓存
        if ($this->images) {
            file_put_contents($this->imagesFile, "<?php\nreturn " . var_export($this->images, true) . ";\n?>");
            if ($this->options['images']) {
                //图片传输
                \think\Console::call('transfer:image', [], 'console');
            } else {
                $this->_showLog('You can type "php mall-cli transfer:images" to transfer images', 'step');
            }
        }
        
        //清理缓存
        $this->_showLog("clear system cache", 'step');
        Cache::clear();

        //处理结束
        $this->_showLog("data transfer end", 'step');
    }
    
    //处理字段一致的表
    private function run_all($table, $oldtable)
    {
        //获取旧数据
        $data = $this->_getData($table, $oldtable);

        //批量写入数据
        $this->_insertAll($table, $data);

        //校验数量是否一致
        $this->_checkCount($table, count($data));
    }
    
    //处理有字段不一致的表
    private function run_common($table, $oldtable, $fields = [])
    {
        $data = $this->_getData($table, $oldtable);
        $allFields = Db::name($table)->getTableInfo('', 'fields');

        $rs = [];
        foreach ($data as $data) {
            $item = [];
            foreach ($allFields as $one) {
                if (isset($fields[$one])) {
                    $rule = $fields[$one];
                    if (is_array($rule)) {
                        if ($rule[0] == 'method') {
                            $method = "_method_" . $rule[1];
                            $args = [$data, $table];
                            if (count($rule) > 2) {
                                $args = array_merge($args, array_slice($rule, 2));
                            }
                            $item[$one] = call_user_func_array([$this, $method], $args);
                        } else if ($rule[0] == 'function') {
                            $method = $rule[1];
                            $value = isset($rule[2]) ? $data[$rule[2]] : $data[$one];
                            $item[$one] = call_user_func($method, $value);
                        }
                    } else {
                        $item[$one] = $data[$rule];
                    }
                } else if (array_key_exists($one, $data)) {
                    $item[$one] = $data[$one];
                }
            }
            $rs[] = $item;
        }

        //批量写入数据
        $this->_insertAll($table, $rs);

        //校验数量是否一致
        $this->_checkCount($table, count($rs));
    }
    
    //修正数字范围
    private function _method_fix_number($data, $table, $col, $mix = 0, $max = 255)
    {
        if (!$data || !isset($data[$col])) {
            return 0;
        }

        $value = intval($data[$col]) ?: 0;
        if ($value < $mix) {
            $value = $mix;
        } else if ($value > $max) {
            $value = $max;
        }

        return $value;
    }

    //富文本内容里的图片路径转换
    private function _method_formate_content($data, $table, $col)
    {
        if (!$data || !isset($data[$col])) {
            return '';
        }

        $value = $data[$col];

        if ($value) {
            $preg = '/src="http:\/\/www\.aiyuangong\.com\/data\/upload\/shop\/store\/goods\/\d+\/(.*?)"/is';
            $rs = [];
            preg_match_all($preg, $value, $rs);
            if (isset($rs[1])) {
                foreach ($rs[1] as $one) {
                    $this->images['_editor']['editor_content'][] = $one;
                }
            }

            $value = preg_replace('/src="http:\/\/www.aiyuangong.com\/data\/upload\/shop\/store\/goods\/(\d+)\/\d+_(\d+)_(\d+)(.*?)"/is', 'src="/uploads/album/$1/$3x$3_$1_$2$4"', $value);
            $value = preg_replace('/src="http:\/\/www.aiyuangong.com\/data\/upload\/shop\/store\/goods\/(\d+)\/(\d+_\d+.*?)"/is', 'src="/uploads/album/$1/$2"', $value);
        }

        return $value;
    }

    //图片路径转换
    private function _method_image_path_formate($data, $table, $tag, $col)
    {
        if (!$data || !isset($data[$col])) {
            return '';
        }

        $value = $data[$col];

        if ($value) {
            $image = $value;
            switch ($tag) {
                case 'goods':
                    $img = explode('_', $value, 2);
                    $value = 'album/' . $img[0] . '/' . $value;
                    break;
                case 'brand':
                    $value = 'brand/' . $value;
                    break;
                case 'order_pay':
                    $value = 'payment/old/' . $value;
                    break;
                case 'joinin':
                    $value = 'joinin/old/' . $value;
                    break;
                case 'waybill':
                    $value = 'waybill/old/' . $value;
                    break;
                case 'member':
                    $this->_showLog('to do member pic', 'error');
                    break;
                case 'store_slide':
                    $value = explode(',', $value);
                    $rs = $image = [];
                    foreach ($value as $value) {
                        if ($value) {
                            $image[] = $value;
                            $rs[] = 'store/slide/' . $value;
                        } else {
                            $rs[] = '';
                        }
                    }
                    $value = implode(',', $rs);
                    break;
                case 'store_label':
                    $value = 'store/label/' . $value;
                    break;
                case 'store_banner':
                    $this->_showLog('to do store_banner pic', 'error');
                    break;
                case 'store_stamp':
                    $this->_showLog('to do store_stamp pic', 'error');
                    break;
                default :
                    $this->_showLog('unknow tag:' . $tag, 'error');
                    break;
            }

            $this->images[$table][$tag][] = $image;
        }

        return $value;
    }

    //结点数据转换
    private function _method_get_cate($data, $table, $tag, $col)
    {
        if (!$data || !isset($data[$col])) {
            $this->_showLog('unknow type_id : [tag : ' . $tag . ', col: ' . $col . ']', 'error');
            return 0;
        }
        $value = intval($data[$col]);

        static $cate = null;
        if (!isset($cate[$tag])) {
            $cate[$tag] = \Fields::getById($tag);
        }

        foreach ($cate[$tag] as $id => $one) {
            if ($one['old_id'] == $value) {
                return $id;
            } else if (isset($one['_child'])) {
                foreach ($one['_child'] as $cid => $two) {
                    if ($two['old_id'] == $value) {
                        return $cid;
                    }
                }
            }
        }
        return 0;
    }
    
    //获取数据
    private function _getData($table, $oldtable)
    {
        //清空数据
        $query = 'TRUNCATE TABLE ' . config('database.prefix') . $table;
        $this->_showLog($query, 'sql');
        Db::execute($query);

        //获取旧表数据
        $data = $this->dbOld->name($oldtable)->select();
        $this->_showLog($this->dbOld->getLastSql(), 'sql');

        if (!$data) {
            return [];
        }

        return $data;
    }
    
    //批量插入数据
    private function _insertAll($table, $data)
    {
        if (!$data) {
            $this->_showLog('no data need to transer', 'step');
            return;
        }
        try {
            $total = count($data);
            $start = 0;
            if ($this->mulInsert < 2) {
                foreach ($data as $data) {
                    $this->_showLog($table . ' transer: ' . $start++ . "/" . $total, 'step');
                    Db::name($table)->insert($data);
                    $this->_showLog(Db::getLastSql(), 'sql');
                }
                $this->_showLog($table . ' transer: ' . $total . "/" . $total, 'step');
            } else {
                while (true) {
                    $insert = array_slice($data, $start, $this->mulInsert);
                    if ($insert) {
                        $this->_showLog($table . ' transer: ' . $start . "/" . $total, 'step');
                        Db::name($table)->insertAll($insert);
                    }
                    if (count($insert) < $this->mulInsert || !$insert) {
                        break;
                    }
                    $this->_showLog(Db::getLastSql(), 'sql');
                    $start += $this->mulInsert;
                }
                $this->_showLog($table . ' transer: ' . $total . "/" . $total, 'step');
            }
        } catch (\Exception $ex) {
            $this->_showLog("error in insert all : [table:" . $table . ", message: " . $ex->getMessage() . " ]", 'error');
            $this->_showLog(Db::getError(), 'sql');
        }
    }
    
    //检查数据一致性
    private function _checkCount($table, $total)
    {
        $count = Db::name($table)->count();
        $this->_showLog(Db::getLastSql(), 'sql');
        if ($count != $total) {
            $this->_showLog('data insert error (' . $count . ' / ' . $total . ')', 'error');
        } else if ($total < 1) {
            $this->_showLog($table . ' no data', 'step');
        }
    }

    //命令行配置说明
    protected function configure()
    {
        $this->setName('transfer:table')
                ->addOption('table', 't', Option::VALUE_OPTIONAL, 'type to exec', null)
                ->addOption('images', 'i', Option::VALUE_OPTIONAL, 'if transfer images', 0)
                ->setDescription('Transfer the tables data of old shop');
    }

}
